// cronjobModel.go
package models

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"myk8s/common"
	"strings"

	"github.com/tidwall/gjson"
	"sigs.k8s.io/yaml"

	yamlutil "k8s.io/apimachinery/pkg/util/yaml"

	batchv1 "k8s.io/api/batch/v1" //注意有的地方要用beta1版本才行
	corev1 "k8s.io/api/core/v1"

	//"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

type Cronjob struct {
	CronjobName      string `json:"cronjobName"`
	NameSpace        string `json:"nameSpace"`
	Labels           string `json:"labels"`
	Annotations      string `json:"annotations"`
	ImgUrl           string `json:"imgUrl"`
	Suspend          string `json:"suspend"`
	Schedule         string `json:"schedule"`
	Active           int    `json:"active"`
	CreateTime       string `json:"createTime"`
	LastScheduleTime string `json:"lastScheduleTime"`
}

func CronjobList(kubeconfig, namespace, cronjobName string, labelsKey, labelsValue string) ([]Cronjob, error) {
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}
	var bbb = make([]Cronjob, 0)

	//设置ListOptions
	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue),
		}
	}

	//BatchV1 会提示the server could not find the requested resource,改成BatchV1beta1 后正常
	//xList, err := common.ClientSet(kubeconfig).BatchV1beta1().CronJobs(namespace).List(context.TODO(), listOptions) //zx-pcauto ok,yt-pcauto异常：the server could not find the requested resource
	xList, err := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace).List(context.TODO(), listOptions) //yt-pcauto ok,zx-pcauto 异常：the server could not find the requested resource

	if err != nil {
		log.Printf("[ERROR] ListCronjobError err:%v", err)
		return bbb, err
	}

	for _, vv := range xList.Items {
		//搜索
		if cronjobName != "" {
			if !strings.Contains(vv.Name, cronjobName) {
				continue
			}
		}

		var labelsStr, imgUrlStr string
		for k1, v1 := range vv.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", k1, v1)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		for _, v2 := range vv.Spec.JobTemplate.Spec.Template.Spec.Containers {
			imgUrlStr += fmt.Sprintf("%s,", v2.Image)
		}

		if len(imgUrlStr) > 0 {
			imgUrlStr = imgUrlStr[0 : len(imgUrlStr)-1]
		}

		var lastScheduleTime string
		if vv.Status.LastScheduleTime != nil {
			lastScheduleTime = vv.Status.LastScheduleTime.Format("2006-01-02 15:04:05")
		}

		xItems := &Cronjob{
			CronjobName: vv.Name,
			NameSpace:   vv.Namespace,
			Labels:      labelsStr,
			ImgUrl:      imgUrlStr,
			Suspend:     fmt.Sprintf("%v", *vv.Spec.Suspend),
			Schedule:    vv.Spec.Schedule,
			Active:      len(vv.Status.Active),
			CreateTime:  vv.CreationTimestamp.Format("2006-01-02 15:04:05"),
			//LastScheduleTime: vv.Status.LastScheduleTime.Format("2006-01-02 15:04:05"),
			LastScheduleTime: lastScheduleTime,
		}
		bbb = append(bbb, *xItems)
	}
	return bbb, err
}

func CronjobCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)

	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	cronjobName := gp.Get("cronjobName").String()
	nameSpace := gp.Get("nameSpace").String()
	var pullPolicy corev1.PullPolicy
	imagePullPolicy := gp.Get("imagePullPolicy").String()
	switch imagePullPolicy {
	case "Never":
		pullPolicy = corev1.PullNever
	case "IfNotPresent":
		pullPolicy = corev1.PullIfNotPresent
	default:
		pullPolicy = corev1.PullAlways
	}
	imageUrl := gp.Get("imageUrl").String()

	schedule := gp.Get("schedule").String()
	if schedule == "" {
		schedule = "* * * * *"
	}

	labelsMap := map[string]string{
		"app": cronjobName,
	}
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").String()] = vv.Get("value").String()
	}

	cronjob := &batchv1.CronJob{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cronjobName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Spec: batchv1.CronJobSpec{
			Schedule: schedule, // 定时任务表达式
			JobTemplate: batchv1.JobTemplateSpec{
				Spec: batchv1.JobSpec{ //v1版本有JobSpec,beta1版本没有
					Template: corev1.PodTemplateSpec{
						Spec: corev1.PodSpec{
							Containers: []corev1.Container{
								{
									Name:            cronjobName,
									Image:           imageUrl,
									ImagePullPolicy: pullPolicy,
								},
							},
							RestartPolicy: corev1.RestartPolicyNever,
						},
					},
				},
			},
		},
	}

	commandStr := gp.Get("command").Str
	if commandStr != "" {
		commandArry := strings.Split(commandStr, ",")
		cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command = commandArry
	}
	argsStr := gp.Get("args").Str
	if argsStr != "" {
		argsArry := strings.Split(argsStr, ",")
		cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Args = argsArry
	}

	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(nameSpace)
	_, err := jobClient.Create(context.TODO(), cronjob, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

func CronjobYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	cronjob := &batchv1.CronJob{}
	err = json.Unmarshal(data, cronjob)
	if err != nil {
		return err
	}

	namespace := cronjob.ObjectMeta.Namespace
	//cronjobName := cronjob.ObjectMeta.Name
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace)
	_, err = jobClient.Update(context.TODO(), cronjob, metav1.UpdateOptions{})
	return err
}

func GetCronjobYaml(kubeconfig, namespace, cronjobName string) (string, error) {
	jobClient := common.ClientSet(kubeconfig).BatchV1().CronJobs(namespace)
	jobInstance, err := jobClient.Get(context.TODO(), cronjobName, metav1.GetOptions{})

	jobUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(jobInstance)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(jobUnstructured)
	if err != nil {
		return "", err
	}
	//fmt.Println(string(yamlBytes))
	return string(yamlBytes), nil
}
